﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Content;

namespace PrisonStep
{
    public class Dalek : BoundingCylinder
    {
        /// <summary>
        /// The model we loaded
        /// </summary>
        protected AnimatedModel animatedModel;



        /// <summary>
        /// The player orientation as a simple angle
        /// </summary>
        protected float orientation = 1.6f + (float)Math.PI;

        public bool InDoor { get { return inDoor; } }
        

        /// <summary>
        /// The transformation matrix.
        /// </summary>
        protected Matrix transform;

        /// <summary>
        /// The current game
        /// </summary>
        protected PrisonGame game;

        protected bool inDoor = false;

        private TimeSpan lastShot;  //stores the time of the last laser shot

        private enum States
        {
            Stance, WalkLeft, WalkRight, TurnLeft, TurnRight, Eating
        }

        
        private PhibesModel phibesModel;
        private string oldRegion = "R_Section1";

        /// <summary>
        /// current state
        /// </summary>
        private States state = States.Stance;

        public Dalek(PrisonGame game, PhibesModel phibesModel)
        {
            location = new Vector3(700, 0, 1053);
            radius = 44;
            height = 135.5f;

            this.game = game;
            this.phibesModel = phibesModel;
            animatedModel = new AnimatedModel(game, "Dalek");

            SetPlayerTransform();
            lastShot = new TimeSpan(0);
        }

        public void LoadContent(ContentManager content)
        {
            animatedModel.LoadContent(content);
        }

        private void CalculateMovement(Matrix playerTransform)
        {
            int eyeBone = animatedModel.Model.Bones["Eye"].Index;
            int plungerArmBone = animatedModel.Model.Bones["PlungerArm"].Index;

            Vector3 walkDirection = playerTransform.Translation - transform.Translation;
            float theta = (float)Math.Atan2(walkDirection.X, walkDirection.Z);
            
            theta -= orientation;
            if (theta < -(float)Math.PI)
                theta += (float)Math.PI * 2;
            //the eye and the PlungerArm should always look at the player (per the project specs)
            animatedModel.changeModifiedTransforms(eyeBone, Matrix.CreateRotationZ(theta));
            animatedModel.changeModifiedTransforms(plungerArmBone, Matrix.CreateRotationZ(theta));
            
            //The alien is already very close to the player 
            if (walkDirection.Length() < 200 && (Math.Abs(theta) < .01))
            {
                state = States.Stance;
            }
            //The alien is already very close to the player or facing more than 22.5 degrees of player
            //only rotate the alien, do not walk
            else if (walkDirection.Length() < 200 || (Math.Abs(theta) > Math.PI/2))
            {
                if (theta < (float)Math.PI && theta > 0)
                {
                    state = States.TurnLeft;
                }
                else
                {
                    state = States.TurnRight;
                }
            }
            //The alien needs to walk towards the player
            else
            {
                if (theta < (float)Math.PI  && theta > 0)
                {
                    state = States.WalkLeft;
                }
                else
                {
                    state = States.WalkRight;
                }
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="gameTime"></param>
        /// <param name="playerTransform"></param>
        /// <param name="leaveDoorsOpen">Indicates if the door should close doors or leave them open</param>
        public void Update(GameTime gameTime, Matrix playerTransform, bool leaveDoorsOpen, Player victoria)
        {
            float delta = (float)gameTime.ElapsedGameTime.TotalSeconds;
            CalculateMovement(playerTransform);
            animatedModel.Update(delta);

            #region State Machine
            int baseBone = animatedModel.Model.Bones["Base"].Index;
            Vector3 newLocation = location;
            switch (state)
            {
                case States.TurnLeft:
                    orientation += delta;
                    break;
                case States.TurnRight:
                    orientation -= delta;
                    break;
                case States.WalkLeft:
                    orientation += delta;
                    newLocation += new Vector3((float)Math.Sin(orientation) * 2, 0, (float)Math.Cos(orientation) * 2);
                    break;
                case States.WalkRight:
                    orientation -= delta;
                    newLocation += new Vector3((float)Math.Sin(orientation) * 2, 0, (float)Math.Cos(orientation) * 2);
                    break;
            }
            
            #endregion

            //newLocation.Y = 0;

            #region Region specific and collision managment

            string region = phibesModel.TestRegion(newLocation);
            if (region.StartsWith("R_Door"))
            {
                inDoor = true;
                foreach (PrisonModel pm in game.PhibesModel)
                {
                    foreach (int door in pm.DoorToBone.Keys)
                    {
                        //if the door region number == the door bone
                        if (Convert.ToInt32(region.Substring(region.Length - 1)) == door)
                        {
                            bool doorOpen = phibesModel.TestDoor(door, pm, newLocation, (float)delta, location, orientation);
                            if ((doorOpen || region != oldRegion))
                                location = newLocation;

                        }
                    }
                }
            }

            else if (region != "")
            {
                location = newLocation;
            }

            if (!region.StartsWith("R_Door"))
            {
                inDoor = false;
            }

            oldRegion = region;

                    
            if (!region.StartsWith("R_Door") && !leaveDoorsOpen)
            {
                //continue closing all doors if they are in the process of closing and the player is not in a door
                foreach (PrisonModel pm in game.PhibesModel)
                {
                    foreach (int door in pm.DoorToBone.Keys)
                    {
                        pm.DoorOpen[door] = false;
                        pm.closeDoor(door, (float)delta);
                    }
                }
            }

            if (region == "R_Section1")
            {
                animatedModel.Section = 1;
            }
            else if (region == "R_Section6")
            {
                animatedModel.Section = 6;
            }
            else if (region == "R_Section5")
            {
                animatedModel.Section = 5;
            }
            else if (region == "R_Section2")
            {
                animatedModel.Section = 2;
            }
            else if (region == "R_Section3")
            {
                animatedModel.Section = 3;
            }
            else if (region == "R_Section4")
            {
                animatedModel.Section = 4;
            }

            #endregion
            SetPlayerTransform();

                

            #region Handle Alien Spit
            //limit the spits to every 3 seconds
            //don't shoot spit when eating a pie
            if ((gameTime.TotalGameTime - lastShot) >= new TimeSpan(0, 0, 0, 0, 3000) &&
                (state != States.Eating))
            {
                int mouthBone = animatedModel.Model.Bones["PlungerArm"].Index;
                Vector3 spitDirection = playerTransform.Translation - transform.Translation;
                float spitOrientation = (float)Math.Atan2(spitDirection.X, spitDirection.Z) - orientation + (float)Math.PI / 2;
                Vector3 spitStartPosition = animatedModel.GetBoneAbsoluteTransform(mouthBone).Translation;
                victoria.SpitFire.Fire(Matrix.CreateRotationY(spitOrientation) * Matrix.CreateTranslation(spitStartPosition) * transform,
                    spitOrientation, spitStartPosition);
                lastShot = gameTime.TotalGameTime;
            }
            #endregion

        }

        public void Draw(GraphicsDeviceManager graphics, GameTime gameTime)
        {
            Matrix transform = Matrix.CreateRotationY(orientation);
            transform.Translation = location;

            animatedModel.Draw(graphics, gameTime, transform);
        }

        /// <summary>
        /// Set the value of transform to match the current location
        /// and orientation.
        /// </summary>
        public void SetPlayerTransform()
        {
            transform = Matrix.CreateRotationY(orientation);
            transform.Translation = location;
        }
    }
}
